%load_ext autoreload
%autoreload 2
import datetime
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from bcb import currency, sgs
import bizdays
MARKET_CALENDAR = bizdays.Calendar.load('ANBIMA.cal')
ACTUAL_CALENDAR = bizdays.Calendar(name='actual')
import myfuncs as my
This trades the future values of exchange rates of Brazilian Reals (BRL) per U.S. Dollar (USD).
The unit price is defined as
where $PTAX_{T-1}$ is the expected value for the U.S. Dollar exchange rate at maturity.
df = pd.read_parquet('data/contracts_202109_202111.parquet').reset_index(drop=True)
dol = df[(df['Mercadoria'] == 'DOL') &
(df['DataRef'] == pd.to_datetime('2021-11-01'))].reset_index(drop=True)
dol['Maturity'] = dol['Vencimento'].map(MARKET_CALENDAR.following)
dol['DU'] = list(MARKET_CALENDAR.vec.bizdays(dol['DataRef'], dol['Maturity']))
dol['DC'] = list(ACTUAL_CALENDAR.vec.bizdays(dol['DataRef'], dol['Maturity']))
dol = dol[dol['DU'] > 0].reset_index(drop=True)
dol['PU'] = dol['PUAtual']/1000
dol_curve = dol[['DataRef', 'Maturity', 'DU', 'DC', 'PU']].copy()
dol_curve
DataRef | Maturity | DU | DC | PU | |
---|---|---|---|---|---|
0 | 2021-11-01 | 2021-12-01 | 20 | 30 | 5.700499 |
1 | 2021-11-01 | 2022-01-03 | 43 | 63 | 5.741469 |
2 | 2021-11-01 | 2022-02-01 | 64 | 92 | 5.782576 |
3 | 2021-11-01 | 2022-03-02 | 83 | 121 | 5.826399 |
4 | 2021-11-01 | 2022-04-01 | 105 | 151 | 5.882861 |
5 | 2021-11-01 | 2022-05-02 | 124 | 182 | 5.932744 |
6 | 2021-11-01 | 2022-06-01 | 146 | 212 | 5.994703 |
7 | 2021-11-01 | 2022-07-01 | 167 | 242 | 6.056054 |
8 | 2021-11-01 | 2022-08-01 | 188 | 273 | 6.112162 |
9 | 2021-11-01 | 2022-09-01 | 211 | 304 | 6.175853 |
10 | 2021-11-01 | 2022-10-03 | 232 | 336 | 6.234773 |
11 | 2021-11-01 | 2022-11-01 | 252 | 365 | 6.288794 |
12 | 2021-11-01 | 2022-12-01 | 272 | 395 | 6.342945 |
13 | 2021-11-01 | 2023-01-02 | 294 | 427 | 6.403784 |
14 | 2021-11-01 | 2023-04-03 | 357 | 518 | 6.573809 |
15 | 2021-11-01 | 2023-07-03 | 418 | 609 | 6.748899 |
16 | 2021-11-01 | 2023-10-02 | 482 | 700 | 6.917391 |
17 | 2021-11-01 | 2024-01-02 | 543 | 792 | 7.072754 |
18 | 2021-11-01 | 2024-04-01 | 604 | 882 | 7.243376 |
19 | 2021-11-01 | 2024-07-01 | 667 | 973 | 7.419898 |
20 | 2021-11-01 | 2024-10-01 | 733 | 1065 | 7.594880 |
21 | 2021-11-01 | 2025-01-02 | 797 | 1158 | 7.790854 |
dol_curve.plot(x='Maturity', y='PU', figsize=(20,6), style='-o',
ylabel='PU', xlabel='Date', title='U.S. Dollar Futures - 2021-11-01');
PTAX = sgs.get(('PTAX', 1), start_date='2021-10-29', end_date='2021-11-01')
PTAX
PTAX | |
---|---|
date | |
2021-10-29 | 5.6430 |
2021-11-01 | 5.6694 |
first_term = pd.DataFrame({
'DataRef': datetime.datetime(2021,11,1).date(),
'Maturity': datetime.datetime(2021,11,3).date(),
'DU': [1],
'DC': [2],
'PU': [PTAX.values[1,0]]
})
dol_curve = pd.concat([first_term, dol_curve], axis=0).reset_index(drop=True)
dol_curve.head()
DataRef | Maturity | DU | DC | PU | |
---|---|---|---|---|---|
0 | 2021-11-01 | 2021-11-03 | 1 | 2 | 5.669400 |
1 | 2021-11-01 | 2021-12-01 | 20 | 30 | 5.700499 |
2 | 2021-11-01 | 2022-01-03 | 43 | 63 | 5.741469 |
3 | 2021-11-01 | 2022-02-01 | 64 | 92 | 5.782576 |
4 | 2021-11-01 | 2022-03-02 | 83 | 121 | 5.826399 |
dol.plot(x='Maturity', y='PU', figsize=(20,6), style='-o',
ylabel='PU', xlabel='Date', title='U.S. Dollar Futures - 2021-11-01');
where
where
where
ddi = df[(df['Mercadoria'] == 'DDI') &
(df['DataRef'] == pd.to_datetime('2021-11-01'))].reset_index(drop=True)
ddi['Maturity'] = ddi['Vencimento'].map(MARKET_CALENDAR.following)
ddi['DC'] = list(ACTUAL_CALENDAR.vec.bizdays(ddi['DataRef'], ddi['Maturity']))
ddi = ddi[ddi['DC'] > 0].reset_index(drop=True)
ddi
DataRef | Mercadoria | CDVencimento | PUAnterior | PUAtual | Variacao | Vencimento | Maturity | DC | |
---|---|---|---|---|---|---|---|---|---|
0 | 2021-11-01 | DDI | Z21 | 99387.51 | 100428.16 | 1040.65 | 2021-12-01 | 2021-12-01 | 30 |
1 | 2021-11-01 | DDI | F22 | 99313.85 | 100357.40 | 1043.55 | 2022-01-01 | 2022-01-03 | 63 |
2 | 2021-11-01 | DDI | G22 | 99269.65 | 100310.70 | 1041.05 | 2022-02-01 | 2022-02-01 | 92 |
3 | 2021-11-01 | DDI | H22 | 99206.95 | 100248.33 | 1041.38 | 2022-03-01 | 2022-03-02 | 121 |
4 | 2021-11-01 | DDI | J22 | 99127.65 | 100168.90 | 1041.25 | 2022-04-01 | 2022-04-01 | 151 |
5 | 2021-11-01 | DDI | K22 | 99053.14 | 100094.12 | 1040.98 | 2022-05-01 | 2022-05-02 | 182 |
6 | 2021-11-01 | DDI | M22 | 98961.99 | 99993.52 | 1031.53 | 2022-06-01 | 2022-06-01 | 212 |
7 | 2021-11-01 | DDI | N22 | 98921.32 | 99951.62 | 1030.30 | 2022-07-01 | 2022-07-01 | 242 |
8 | 2021-11-01 | DDI | Q22 | 98774.23 | 99794.91 | 1020.68 | 2022-08-01 | 2022-08-01 | 273 |
9 | 2021-11-01 | DDI | U22 | 98658.94 | 99676.78 | 1017.84 | 2022-09-01 | 2022-09-01 | 304 |
10 | 2021-11-01 | DDI | V22 | 98532.99 | 99548.45 | 1015.46 | 2022-10-01 | 2022-10-03 | 336 |
11 | 2021-11-01 | DDI | X22 | 98407.45 | 99419.39 | 1011.94 | 2022-11-01 | 2022-11-01 | 365 |
12 | 2021-11-01 | DDI | Z22 | 98271.09 | 99281.04 | 1009.95 | 2022-12-01 | 2022-12-01 | 395 |
13 | 2021-11-01 | DDI | F23 | 98131.84 | 99138.07 | 1006.23 | 2023-01-01 | 2023-01-02 | 427 |
14 | 2021-11-01 | DDI | J23 | 97719.06 | 98728.57 | 1009.51 | 2023-04-01 | 2023-04-03 | 518 |
15 | 2021-11-01 | DDI | N23 | 97321.13 | 98387.21 | 1066.08 | 2023-07-01 | 2023-07-03 | 609 |
16 | 2021-11-01 | DDI | V23 | 96811.74 | 97823.96 | 1012.22 | 2023-10-01 | 2023-10-02 | 700 |
17 | 2021-11-01 | DDI | F24 | 96348.47 | 97298.60 | 950.13 | 2024-01-01 | 2024-01-02 | 792 |
18 | 2021-11-01 | DDI | J24 | 95891.87 | 96830.54 | 938.67 | 2024-04-01 | 2024-04-01 | 882 |
19 | 2021-11-01 | DDI | N24 | 95412.70 | 96316.44 | 903.74 | 2024-07-01 | 2024-07-01 | 973 |
20 | 2021-11-01 | DDI | V24 | 94860.29 | 95721.22 | 860.93 | 2024-10-01 | 2024-10-01 | 1065 |
21 | 2021-11-01 | DDI | F25 | 94333.25 | 95151.82 | 818.57 | 2025-01-01 | 2025-01-02 | 1158 |
22 | 2021-11-01 | DDI | J25 | 93806.32 | 94605.98 | 799.66 | 2025-04-01 | 2025-04-01 | 1247 |
23 | 2021-11-01 | DDI | N25 | 93349.45 | 94136.13 | 786.68 | 2025-07-01 | 2025-07-01 | 1338 |
24 | 2021-11-01 | DDI | V25 | 92852.42 | 93653.47 | 801.05 | 2025-10-01 | 2025-10-01 | 1430 |
25 | 2021-11-01 | DDI | F26 | 92339.55 | 93128.87 | 789.32 | 2026-01-01 | 2026-01-02 | 1523 |
26 | 2021-11-01 | DDI | J26 | 91916.14 | 92690.98 | 774.84 | 2026-04-01 | 2026-04-01 | 1612 |
27 | 2021-11-01 | DDI | N26 | 91400.18 | 92161.19 | 761.01 | 2026-07-01 | 2026-07-01 | 1703 |
28 | 2021-11-01 | DDI | V26 | 90959.74 | 91705.42 | 745.68 | 2026-10-01 | 2026-10-01 | 1795 |
29 | 2021-11-01 | DDI | F27 | 90419.84 | 91151.68 | 731.84 | 2027-01-01 | 2027-01-04 | 1890 |
30 | 2021-11-01 | DDI | F28 | 88407.09 | 89139.17 | 732.08 | 2028-01-01 | 2028-01-03 | 2254 |
31 | 2021-11-01 | DDI | F29 | 86348.93 | 87198.44 | 849.51 | 2029-01-01 | 2029-01-02 | 2619 |
32 | 2021-11-01 | DDI | F30 | 84188.92 | 85246.26 | 1057.34 | 2030-01-01 | 2030-01-02 | 2984 |
33 | 2021-11-01 | DDI | F31 | 82001.99 | 83049.38 | 1047.39 | 2031-01-01 | 2031-01-02 | 3349 |
34 | 2021-11-01 | DDI | F33 | 76893.80 | 77966.07 | 1072.27 | 2033-01-01 | 2033-01-03 | 4081 |
35 | 2021-11-01 | DDI | F35 | 71822.63 | 72930.33 | 1107.70 | 2035-01-01 | 2035-01-02 | 4810 |
36 | 2021-11-01 | DDI | F36 | 69282.50 | 70363.69 | 1081.19 | 2036-01-01 | 2036-01-02 | 5175 |
37 | 2021-11-01 | DDI | F37 | 66234.46 | 67270.73 | 1036.27 | 2037-01-01 | 2037-01-02 | 5541 |
ddi['Rate'] = (100000 / ddi['PUAtual'] - 1)*(360 / ddi['DC'])
ddi_curve = ddi[['DataRef', 'Maturity', 'DC', 'Rate']]
ddi_curve.plot(x='Maturity', y='Rate', figsize=(20,6), style='-o',
ylabel='Rate', xlabel='Date', title='DDI Curve - 2021-11-01')
plt.axhline(y=0, ls='--', color='grey')
plt.show()
ddi_curve.head()
DataRef | Maturity | DC | Rate | |
---|---|---|---|---|
0 | 2021-11-01 | 2021-12-01 | 30 | -0.05116 |
1 | 2021-11-01 | 2022-01-03 | 63 | -0.02035 |
2 | 2021-11-01 | 2022-02-01 | 92 | -0.01212 |
3 | 2021-11-01 | 2022-03-02 | 121 | -0.00737 |
4 | 2021-11-01 | 2022-04-01 | 151 | -0.00402 |
PTAX
PTAX | |
---|---|
date | |
2021-10-29 | 5.6430 |
2021-11-01 | 5.6694 |
CDI = sgs.get(('CDI', 4389), start_date='2021-11-01', end_date='2021-11-01')
CDI
CDI | |
---|---|
date | |
2021-11-01 | 7.65 |
cdi = CDI.values[0,0]
ptax_0 = PTAX.values[1,0] # PTAX(t) for the current business day
ptax_1 = PTAX.values[0,0] # PTAX(t-1) for the previous business day
DC = 2 # total number of days between the reference date and one business day ahead
first_term = pd.DataFrame({
'DataRef': datetime.datetime(2021,11,1).date(),
'Maturity': datetime.datetime(2021,11,3).date(),
'DC': [DC],
'Rate': [(((1 + cdi/100)**(1/252)) / (ptax_0/ptax_1) - 1) * 360/DC]
})
ddi_curve = pd.concat([first_term, ddi_curve], axis=0).reset_index(drop=True)
ddi_curve.plot(x='Maturity', y='Rate', figsize=(20,6), style='-o',
ylabel='Rate', xlabel='Date', title='DDI Curve - 2021-11-01')
plt.axhline(y=0, ls='--', color='grey')
plt.show()
ddi_hist = df[(df['Mercadoria'] == 'DDI') & (df['PUAtual'] != 100000.0)].copy()
ddi_hist['VencimentoAdj'] = ddi_hist['Vencimento'].map(MARKET_CALENDAR.following)
ddi_hist['DU'] = list(MARKET_CALENDAR.vec.bizdays(ddi_hist['DataRef'], ddi_hist['VencimentoAdj']))
ddi_hist['DC'] = list(ACTUAL_CALENDAR.vec.bizdays(ddi_hist['DataRef'], ddi_hist['VencimentoAdj']))
ddi_hist['Taxa'] = (100000 / ddi_hist['PUAtual'] - 1)/(ddi_hist['DC'] / 360)
ddi_hist_first = ddi_hist.groupby('DataRef').apply(lambda x: x.sort_values('DU').iloc[0])
ddi_hist_m1 = ddi_hist_first.set_index('DataRef')['Taxa']
ddi_hist_second = ddi_hist.groupby('DataRef').apply(lambda x: x.sort_values('DU').iloc[1])
ddi_hist_m2 = ddi_hist_second.set_index('DataRef')['Taxa']
ddi_hist_x = pd.concat((ddi_hist_m1, ddi_hist_m2), axis=1)
ddi_hist_x.columns = ('First maturity', 'Second maturity')
ddi_hist_x.plot(figsize=(15,6));
and
$$ PTAX_{t} = DOCL_{t} \frac{(1 + CDI_t)^{DU(t,t+1)/252}}{1 + CL(t,t+1) \cdot \frac{DC(t,t+1)}{360}} $$where
frc = df[(df['Mercadoria'] == 'FRC') &
(df['DataRef'] == pd.to_datetime('2021-11-01'))].copy()
frc['Maturity'] = frc['Vencimento'].map(MARKET_CALENDAR.following)
frc['DU'] = frc.apply(lambda x: MARKET_CALENDAR.bizdays(x['DataRef'], x['Maturity']), axis=1)
frc['DC'] = frc.apply(lambda x: ACTUAL_CALENDAR.bizdays(x['DataRef'], x['Maturity']), axis=1)
frc = frc[frc['DU'] > 0].reset_index(drop=True)
frc.head()
DataRef | Mercadoria | CDVencimento | PUAnterior | PUAtual | Variacao | Vencimento | Maturity | DU | DC | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 2021-11-01 | FRC | F22 | 0.81 | 0.77 | -0.04 | 2022-01-01 | 2022-01-03 | 43 | 63 |
1 | 2021-11-01 | FRC | G22 | 0.69 | 0.68 | -0.01 | 2022-02-01 | 2022-02-01 | 64 | 92 |
2 | 2021-11-01 | FRC | H22 | 0.72 | 0.71 | -0.01 | 2022-03-01 | 2022-03-02 | 83 | 121 |
3 | 2021-11-01 | FRC | J22 | 0.78 | 0.77 | -0.01 | 2022-04-01 | 2022-04-01 | 105 | 151 |
4 | 2021-11-01 | FRC | K22 | 0.80 | 0.79 | -0.01 | 2022-05-01 | 2022-05-02 | 124 | 182 |
dol_cl = 5.6699
ptax_1 = PTAX.values[0,0] # PTAX(t-1) for the previous business day
cs = ddi_curve['Rate'].iloc[1] # CUPOM CAMBIAL SUJO for the first future
dc = ddi_curve['DC'].iloc[1]
_f_cl = (dol_cl/ptax_1) * (1 + cs*dc/360)
_frc = (1 + (frc['PUAtual']/100)*((frc['DC'] - dc)/360))
frc['Rate'] = (_f_cl * _frc - 1) * (360/frc['DC'])
frc.head()
DataRef | Mercadoria | CDVencimento | PUAnterior | PUAtual | Variacao | Vencimento | Maturity | DU | DC | Rate | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2021-11-01 | FRC | F22 | 0.81 | 0.77 | -0.04 | 2022-01-01 | 2022-01-03 | 43 | 63 | 0.006797 |
1 | 2021-11-01 | FRC | G22 | 0.69 | 0.68 | -0.01 | 2022-02-01 | 2022-02-01 | 64 | 92 | 0.006476 |
2 | 2021-11-01 | FRC | H22 | 0.72 | 0.71 | -0.01 | 2022-03-01 | 2022-03-02 | 83 | 121 | 0.006780 |
3 | 2021-11-01 | FRC | J22 | 0.78 | 0.77 | -0.01 | 2022-04-01 | 2022-04-01 | 105 | 151 | 0.007325 |
4 | 2021-11-01 | FRC | K22 | 0.80 | 0.79 | -0.01 | 2022-05-01 | 2022-05-02 | 124 | 182 | 0.007557 |
frc_curve = frc[['DataRef', 'Maturity', 'DC', 'Rate']].copy()
frc_curve.plot(x='Maturity', y='Rate', figsize=(20,6), style='-o',
ylabel='Rate', xlabel='Date', title='FRC Curve - 2021-11-01')
plt.show()